﻿using GSPatEditor.Pat;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GSPatEditor.Simulator
{
    enum ActorType
    {
        Player,
        Enemy,
    }

    delegate void ActorLabel(Actor actor);

    enum ActorVariableType
    {
        //Integer,
        Float,
        ActorLabel,
        //ActorReference,
    }

    class ActorVariable
    {
        public ActorVariableType Type;
        public object Value;
    }

    class Actor
    {
        public event Action<Actor> BeforeRelease, AfterRelease;

        //world
        public World World { get; private set; }
        public bool IsReleased { get; private set; }

        //rendering
        public int Priority;
        public float ScaleX, ScaleY;
        public float Rotation;

        //position
        public float DefaultGravity;
        public float X, Y;
        public float VX, VY;
        public float? Gravity;
        public bool ImmuneGravity;

        //collision
        public bool CollisionEnabled;
        public bool IsInAir;

        //animation
        public ActionList Actions { get; private set; }
        public Animation CurrentAnimation { get; private set; }
        public int CurrentFrameIndex { get; private set; }
        public int CurrentKeyFrameIndex { get; private set; }
        private int KeyEndIndex;
        private int frameCounter;

        //count, used in Action Code
        public int ActionCount;

        public Frame CurrentFrame
        {
            get
            {
                return CurrentAnimation.FrameList[CurrentFrameIndex];
            }
        }

        //also update ActorActions.ClearLabel
        public ActorLabel UpdateLabel;
        public ActorLabel SitLabel;
        public ActorLabel FallLabel; //not used in simulation
        public ActorLabel EndMotionLabel;
        public ActorLabel[] EndKeyFrameLabel;
        public ActorLabel HitEvent; //bullet only

        public Dictionary<string, ActorVariable> Variables = new Dictionary<string, ActorVariable>();

        private Actor()
        {
            this.ScaleX = 1;
            this.ScaleY = 1;

            this.ImmuneGravity = true;

            this.IsInAir = true;
        }

        public Actor(World theWorld) : this()
        {
            this.World = theWorld;
        }

        public void SetActionList(ActionList actions)
        {
            this.Actions = actions;
        }

        public void SetMotion(Animation animation, int key)
        {
            CurrentAnimation = animation;
            frameCounter = 0;

            var keyFrameInfo = animation.KeyFrameIndexList[key];
            CurrentFrameIndex = keyFrameInfo.FrameIndex;
            CurrentKeyFrameIndex = key;

            KeyEndIndex = key == animation.KeyFrameIndexList.Count - 1 ?
                animation.FrameList.Count : animation.KeyFrameIndexList[key + 1].FrameIndex;
        }

        public void Update()
        {
            UpdateGravity();
            StepAnimation();
            if (UpdateLabel != null)
            {
                UpdateLabel(this);
            }
        }

        private void UpdateGravity()
        {
            if (!ImmuneGravity)
            {
                if (Gravity.HasValue)
                {
                    VY += Gravity.Value;
                }
                else
                {
                    VY += DefaultGravity;
                }
                if (VY > 15)
                {
                    VY = 15;
                }
            }
        }

        private void StepAnimation()
        {
            if (++frameCounter == CurrentFrame.FrameCount)
            {
                ++CurrentFrameIndex;
                frameCounter = 0;

                if (CurrentFrameIndex == KeyEndIndex)
                {
                    //TODO end key event
                    if (CurrentAnimation.KeyFrameIndexList[CurrentKeyFrameIndex].IsLoop)
                    {
                        CurrentFrameIndex = CurrentAnimation.KeyFrameIndexList[CurrentKeyFrameIndex].FrameIndex;
                    }
                    else
                    {
                        if (CurrentKeyFrameIndex == CurrentAnimation.KeyFrameIndexList.Count - 1)
                        {
                            //this.Release();
                            if (this.EndMotionLabel != null)
                            {
                                this.EndMotionLabel(this);
                            }
                            else
                            {
                                this.Release();
                            }
                        }
                        else
                        {
                            ++CurrentKeyFrameIndex;
                            KeyEndIndex = CurrentKeyFrameIndex == CurrentAnimation.KeyFrameIndexList.Count - 1 ?
                                CurrentAnimation.FrameList.Count :
                                CurrentAnimation.KeyFrameIndexList[CurrentKeyFrameIndex + 1].FrameIndex;

                            //run take callback
                            if (this.EndKeyFrameLabel != null && this.EndKeyFrameLabel.Length >= CurrentKeyFrameIndex)
                            {
                                this.EndKeyFrameLabel[CurrentKeyFrameIndex - 1](this);
                            }
                        }
                    }
                }
            }
        }

        public void Release()
        {
            if (BeforeRelease != null)
            {
                BeforeRelease(this);
            }

            World.RemoveActor(this);
            IsReleased = true;

            if (AfterRelease != null)
            {
                AfterRelease(this);
            }
        }
    }
}
